package com.gitee.funcy.multiple.datasource.aspectj;

import com.gitee.funcy.multiple.datasource.aspectj.annotation.DataSourceDeclare;
import com.gitee.funcy.multiple.datasource.datasource.AppStorageContextHolder;
import com.gitee.funcy.multiple.datasource.util.ReflectUtils;
import com.gitee.funcy.multiple.datasource.datasource.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 多数据源处理
 *
 * @author funcy
 */
@Slf4j
@Aspect
@Order(1)
@Component
public class DataSourceDeclareAspect {

    @Pointcut("@annotation(com.gitee.funcy.multiple.datasource.aspectj.annotation.DataSourceDeclare)"
            + "|| @within(com.gitee.funcy.multiple.datasource.aspectj.annotation.DataSourceDeclare)"
            + "|| execution(* com.gitee.funcy.multiple.datasource.mapper..*.*(..))"
    )
    public void dsPointCut() {

    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        DataSourceDeclare dataSource = getDataSource(point);
        if(null != dataSource) {
            DynamicDataSourceContextHolder.setDataSourceType(AppStorageContextHolder.getApp());
        }

        try {
            return point.proceed();
        } finally {
            // 销毁数据源 在执行方法之后
            if(null != dataSource) {
                DynamicDataSourceContextHolder.clearDataSourceType();
            }
        }
    }

    /**
     * 获取需要切换的数据源
     */
    public DataSourceDeclare getDataSource(ProceedingJoinPoint point) {
        MethodSignature signature = (MethodSignature) point.getSignature();

        //1.获取方法上的DataSource
        Method method = signature.getMethod();
        DataSourceDeclare dataSource = method.getAnnotation(DataSourceDeclare.class);
        if(null != dataSource) {
            return dataSource;
        }

        //2.获取声明类的DataSource
        dataSource = method.getDeclaringClass().getAnnotation(DataSourceDeclare.class);
        if(null != dataSource) {
            return dataSource;
        }

        //3.获取目标类上的DataSourceDeclare
        Class<? extends Object> targetClass = point.getTarget().getClass();
        dataSource = targetClass.getAnnotation(DataSourceDeclare.class);
        if (null != dataSource) {
            return dataSource;
        }

        //反射获取
        /*
         * point.getTarget()（为com.baomidou.mybatisplus.core.override.MybatisMapperProxy的实例）的结构如下：
         *
         * point.getTarget()
         * |-h
         *   |-sqlSession
         *   |-mapperInterface
         *   |-methodCache
         *
         */
        Object h = ReflectUtils.getFieldValue(point.getTarget(), "h");
        if(null != h) {
            Class<? extends Object> clazz = ReflectUtils.getFieldValue(h, "mapperInterface");
            if (null != clazz) {
                dataSource = clazz.getAnnotation(DataSourceDeclare.class);
                if (null != dataSource) {
                    return dataSource;
                }
            }
        }

        log.warn("未找到对应的DataSource，方法：{}，声明类：{}，目标类：{}", method.getName(),
                method.getDeclaringClass().getName(), targetClass.getName());
        return null;
    }
}
